package com.bmslz.monitorpre.job;

import com.bmslz.monitorpre.grpc.MonitorGrpcClient;
import com.sun.management.OperatingSystemMXBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import oshi.SystemInfo;
import oshi.hardware.*;
import oshi.software.os.OperatingSystem;
import oshi.util.GlobalConfig;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class PushDataJob {
    @Autowired
    private MonitorGrpcClient monitorGrpcClient;


    /**
     * 测点数据推送demo
     * map中是监控模型的数据
     * 每一个key对应模型中的一个测点key,value为当前测点数据
     * Map<String, String> data = new HashMap<>();
     * data.put("cpu.use", "1");
     * data.put("mem.use", "100");
     * monitorGrpcClient.sendMetrics(data);
     * return "Metrics sent";
     */
    @Scheduled(fixedRateString = "#{${client.project.rate} * 1000}")
    public void executeTask() {
        //下面写你测点数据的获取逻辑:请求远程接口\获取本机信息\获取其他组件暴露的信息等方式都可以
        log.info("开始获取系统参数");
        long start = System.currentTimeMillis();
        Map<String, String> map = getData();
        log.info("获取完成,耗时-{}毫秒", System.currentTimeMillis() - start);
        monitorGrpcClient.sendMetrics(map);
    }

    private Map<String, String> getData() {
        Map<String, String> map = new HashMap<>();
        try {
            SystemInfo systemInfo = new SystemInfo();
            GlobalMemory memory = systemInfo.getHardware().getMemory();

            OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
            // 总的物理内存
            String totalMemorySize = new DecimalFormat("#.##").format(memory.getTotal() / 1024.0 / 1024 / 1024);
            // 已使用的物理内存
            String usedMemory = new DecimalFormat("#.##").format((memory.getTotal() - memory.getAvailable()) / 1024.0 / 1024 / 1024);
            // 获得线程总数
            ThreadGroup parentThread;
            for (parentThread = Thread.currentThread().getThreadGroup(); parentThread
                    .getParent() != null; parentThread = parentThread.getParent()) {

            }

            int totalThread = parentThread.activeCount();

            // 磁盘使用情况
            File[] files = File.listRoots();
            Double total = 0d;
            Double un = 0d;
            for (File file : files) {
                total += file.getTotalSpace() * 1.0 / 1024 / 1024 / 1024;
                un += file.getUsableSpace() * 1.0 / 1024 / 1024 / 1024;
            }
            String diskUse = String.valueOf((int) Math.floor(((total - un) / total) * 100));

            // todo 解决 Why does OSHI's System and Processor CPU usage differ from the Windows Task Manager?
            GlobalConfig.set(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY, true);

            CentralProcessor processor = systemInfo.getHardware().getProcessor();
            long[] prevTicks = processor.getSystemCpuLoadTicks();
            // 睡眠1s
            TimeUnit.SECONDS.sleep(1);
            long[] ticks = processor.getSystemCpuLoadTicks();
            long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
            long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
            long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
            long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
            long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
            long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
            long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
            long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
            long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
            // todo cup利用
            //  user + system + nice + iowait + irq + softirq + steal
            long cpuUtilization = user + nice + cSys + iowait + irq + softirq + steal;

            HardwareAbstractionLayer hardware = systemInfo.getHardware();
            // 获取所有网络接口:cite[6]
            List<NetworkIF> before = hardware.getNetworkIFs();

            // 休眠一段时间以便计算流量变化:cite[1]
            TimeUnit.SECONDS.sleep(1);

            // 2. 更新属性后，再次获取网络接口信息以计算流量:cite[1]
            hardware.getNetworkIFs().forEach(NetworkIF::updateAttributes);
            List<NetworkIF> after = hardware.getNetworkIFs();

            for (NetworkIF netBefore : before) {
                String ifName = netBefore.getName();
                for (NetworkIF netAfter : after) {
                    if (ifName.equals(netAfter.getName())) {
                        long rxBytesBefore = netBefore.getBytesRecv();
                        long txBytesBefore = netBefore.getBytesSent();
                        long rxBytesAfter = netAfter.getBytesRecv();
                        long txBytesAfter = netAfter.getBytesSent();

                        // 计算速率 (字节/秒):cite[1]
                        long rxRate = (rxBytesAfter - rxBytesBefore) / 5;
                        long txRate = (txBytesAfter - txBytesBefore) / 5;
                        map.put("net-" + netAfter.getName() + ".in", String.format("%.2f", rxRate / 1024.0));
                        map.put("net-" + netAfter.getName() + ".out", String.format("%.2f", txRate / 1024.0));
                        break;
                    }
                }
            }

            map.put("disk.total", String.format("%.2f", total));
            map.put("disk.un", String.format("%.2f", un));
            map.put("disk.use", diskUse);
            map.put("cpu.c_num", String.valueOf(Runtime.getRuntime().availableProcessors()));
            map.put("cpu.use", String.valueOf(String.format("%.2f", (cpuUtilization * 1.0 / totalCpu) * 100)));
            map.put("mem.total", totalMemorySize);
            map.put("mem.used", usedMemory);
            map.put("mem.use_rate", new BigDecimal(usedMemory).divide(new BigDecimal(totalMemorySize), RoundingMode.CEILING).multiply(new BigDecimal("100")).toString());
            log.info("map-{}", map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }


    /**
     * 将字节数转换为更易读的格式 (B/s, KB/s, MB/s):cite[1]
     */
    private static String formatBytes(long bytesPerSec) {
        if (bytesPerSec < 1024) {
            return bytesPerSec + " B";
        } else if (bytesPerSec < 1024 * 1024) {
            return String.format("%.2f KB", bytesPerSec / 1024.0);
        } else {
            return String.format("%.2f MB", bytesPerSec / (1024.0 * 1024.0));
        }
    }
}

